﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;
using Telerik.Workflow;
using Telerik.Cms.Web.UI;
using System.Net.Mail;
using System.Web.Security;
using Telerik.Cms.Engine;
using Telerik.Cms.Engine.Security;
using Telerik.Workflow.WebControls;
using System.Web.UI.WebControls;
using Telerik;
using Telerik.Security.Permissions;

namespace SelArom.Net.Sitefinity.Templates
{
	public class ContentItemPreviewBase : UserControl
	{
		protected ContentManager mgr;
		protected IContent contentItem;
		protected MembershipUser usr;
		protected WorkflowMenu wfButton;
		protected string wfCommand;
		protected string DefaultProvider;

		protected void Page_Load(object sender, EventArgs e)
		{
			// ensure manager is initialized
			if (mgr == null) return;

			// handle workflow change
			wfButton = FindControl("workflowMenu") as WorkflowMenu;
			if (wfButton == null) return;
			wfButton.Command += workflowMenu_Command;
		}

		protected virtual string ForApprovalMessage()
		{
			return string.Empty;
		}

		protected virtual string ApprovedMessage()
		{
			return string.Empty;
		}

		protected void workflowMenu_Command(object sender, CommandEventArgs e)
		{
			// make sure the default provider was supplied
			if (string.IsNullOrEmpty(DefaultProvider)) return;

			// get workflow
			var workflow = ((WorkflowMenu)sender).GetWorkflow();
			if (workflow == null) return;

			// get item data from workflow
			var contentWorkFlow = workflow.Activity as ContentWorkflow;
			contentItem = contentWorkFlow != null ? mgr.GetContent(contentWorkFlow.ItemId) : null;

			// populate workflow commands
			var commands = new List<EventActivity>();
			LoadCommands(commands, workflow.Activity.Activities);
			var idx = int.Parse((string)e.CommandArgument);
			wfCommand = commands[idx].CommandName;

			// make sure there are events to handle
			if (commands.Count <= idx) return;

			// don't send emails to administrators
			if (Page.User.IsInRole("administrators")) return;

			// get user data and create message
			usr = Membership.GetUser();
			var mailMsg = new MailMessage();
			mailMsg.From = new MailAddress(usr.Email);

			// retrieve module instance and permissions
			var mod = ModuleManager.WebModules[DefaultProvider.ToLower()] as SecuredModule;
			var sec = (mod.SecurityRoots[DefaultProvider] as Telerik.Security.Permissions.ISecured);

			// iterate through all user roles to see if they have permission to publish and approve
			var userRoles = Roles.GetRolesForUser(usr.UserName);

			bool canPublish = false, canApprove = false;
			foreach (var role in userRoles)
			{
				// if both are true just exit
				if (canPublish && canApprove) break;

				// check permissions for the role
				var perms = sec.GetPermission(role.ToLower());

				// only modify if we haven't already found grant permissions
				if (!canPublish) canPublish = (perms.Grant & WorkflowRights.Publish) == WorkflowRights.Publish;
				if (!canApprove) canApprove = (perms.Grant & WorkflowRights.Approve) == WorkflowRights.Approve;
			}

			// lists to create emails
			var roles = new List<string>();
			var users = new List<string>();

			switch (wfCommand.ToLower())
			{
				case "approve":

					#region ApproveCommand
					// don't send email if user is a publisher
					if (canPublish) return;

					// retrieve roles that can publish this module
					roles = new List<string>();
					foreach (var perm in sec.Permissions)
					{
						if ((perm.Value.Grant & WorkflowRights.Publish) == WorkflowRights.Publish)
							roles.Add(perm.Key);
					}

					// retreieve users in the publishiner role
					users = new List<string>();

					foreach (var role in roles)
					{
						// for some reason non-existing roles can appear
						// likely because roles have been deleted but not from the permission base
						// this command ensures that the actual role exists!
						if (!Roles.RoleExists(role)) continue;

						foreach (var user in Roles.GetUsersInRole(role))
							// only add publishers that are also in the module role
							if (Roles.IsUserInRole(user, DefaultProvider))
								users.Add(user);
					}

					// add users emails to message
					foreach (var user in users)
					{
						// this shouldn't happen, but if it does skip the current user
						if (user == usr.UserName) continue;

						var member = Membership.GetUser(user);
						mailMsg.To.Add(member.Email);
					}

					// save message subject and body
					mailMsg.Subject = DefaultProvider + " item approved for publication";
					mailMsg.Body = ApprovedMessage();
					break;
					#endregion

				case "sendforapproval":
					#region ForApprovalCommand
					// don't send email if user is an approver
					if (canApprove) return;

					// retrieve roles that can publish this module

					foreach (var perm in sec.Permissions)
					{
						if ((perm.Value.Grant & WorkflowRights.Approve) == WorkflowRights.Approve)
							roles.Add(perm.Key);
					}

					// retreieve users in the approver roles

					foreach (var role in roles)
					{
						// for some reason non-existing roles can appear
						// likely because roles have been deleted but not from the permission base
						// this command ensures that the actual role exists!
						if (!Roles.RoleExists(role)) continue;

						foreach (var user in Roles.GetUsersInRole(role))
							// only add approvers that are also in the News module role
							if (Roles.IsUserInRole(user, DefaultProvider))
								users.Add(user);
					}

					// add users emails to message
					foreach (var user in users)
					{
						// this shouldn't happen, but if it does skip the current user
						if (user == usr.UserName) continue;

						var member = Membership.GetUser(user);
						mailMsg.To.Add(member.Email);
					}

					// save message subject and body
					mailMsg.Subject = DefaultProvider + " item sent for approval";
					mailMsg.Body = ForApprovalMessage();
					break;
					#endregion

				default:
					return;
			}

			// make sure there is a recipient
			if (mailMsg.To.Count == 0) return;

			// make sure message control exists on the page
			var msg = FindControl("message1") as MessageControl ?? new MessageControl();

			try
			{
				// Init SmtpClient and send   
				var smtpClient = new SmtpClient();
				smtpClient.Send(mailMsg);
				msg.Message = "Status Notification Email Sent Successfully!";
				var sb = new StringBuilder();
				sb.Append("<div style=\"display: none; visibility: hidden;\">");
				foreach (var email in mailMsg.To)
					sb.AppendFormat("{0}<br />", email.Address);
				sb.Append("</div>");
				msg.Message += sb.ToString();
			}
			catch (Exception ex)
			{
				// todo log exception
				msg.Mode = MessageMode.Error;
				msg.Message = "Command executed successfully, however the notification email was not sent. Please manually notify users of status change.";
				var sb = new StringBuilder();
				sb.Append("<div style=\"display: none; visibility: hidden;\">");
				sb.Append(ex.ToString());
				foreach (var email in mailMsg.To)
					sb.AppendFormat("<br />{0}", email.Address);
				sb.Append("</div>");
				msg.Message += sb.ToString();
			}
		}

		protected void LoadCommands(List<EventActivity> commands, IList<Activity> activities)
		{
			foreach (var act in activities)
			{
				if (act is EventActivity)
					commands.Add((EventActivity)act);
				LoadCommands(commands, act.Activities);
			}
		}
	}
}
